#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <fcntl.h>
#include <unistd.h>
#include <linux/fb.h>
#include <linux/vt.h>
#include <linux/kd.h>
#include <sys/mman.h>
#include <asm/ioctls.h>
#include <errno.h>
static int OriginalVT = -1;
struct fb_fix_screeninfo FixedInfo;
struct fb_var_screeninfo OrigVarInfo;
struct fb_var_screeninfo newInfo;
static int FrameBufferFD = -1;
void *FrameBuffer = (void *) -1;
static int ConsoleFD = -1;
int ulVideoMemByteSize;

#define LocalPrint fprintf

#define SIGUSR1 10
int openFBDEV(void) {
    int ret;
    /* open the framebuffer device */
    FrameBufferFD = open("/dev/graphics/fb0", O_RDWR);
    if (FrameBufferFD < 0) {
        LocalPrint(stderr, "Error opening /dev/graphics/fb0\n");
        return -1;
    }

    /* Get the fixed screen info */
    if (ioctl(FrameBufferFD, FBIOGET_FSCREENINFO, &FixedInfo)) {
        LocalPrint(stderr, "error: ioctl(FBIOGET_FSCREENINFO) failed\n");
        return -1;
    }
    /* get the variable screen info */
    if (ioctl(FrameBufferFD, FBIOGET_VSCREENINFO, &OrigVarInfo)) {
        LocalPrint(stderr, "error: ioctl(FBIOGET_VSCREENINFO) failed\n");
        return -1;
    }

    if (FixedInfo.visual != FB_VISUAL_TRUECOLOR && FixedInfo.visual != FB_VISUAL_DIRECTCOLOR) {
        LocalPrint(stderr, "non-TRUE/DIRECT-COLOR visuals (0x%x) not supported by this demo.\n", FixedInfo.visual);
        return -1;
    }

    LocalPrint(stderr, "OrigVarInfo:\n\t\txres = %d yres = %d bits_per_pixel = %d xres_virtual=%d, yres_virtual=%d\n",
        OrigVarInfo.xres,
        OrigVarInfo.yres,
        OrigVarInfo.bits_per_pixel, OrigVarInfo.xres_virtual, OrigVarInfo.yres_virtual );
    LocalPrint(stderr, "\t\txoffset:%d yoffset:%d line_length: %d\n",
       OrigVarInfo.xoffset,
       OrigVarInfo.yoffset,
       FixedInfo.line_length );


    newInfo = OrigVarInfo;
  	newInfo.bits_per_pixel = 32;
//	newInfo.xres = newInfo.xres_virtual = 1440;
//	newInfo.yres = newInfo.yres_virtual = 848;
//	newInfo.activate = FB_ACTIVATE_NOW;
    if (ret=ioctl(FrameBufferFD, FBIOPUT_VSCREENINFO, &newInfo)) {
        LocalPrint(stderr, "error: ioctl(FBIOPUT_VSCREENINFO) failed:%s\n",strerror(errno));
        return -1;
    }

/* get the variable screen info */
    if (ioctl(FrameBufferFD, FBIOGET_VSCREENINFO, &newInfo)) {
        LocalPrint(stderr, "error: ioctl(FBIOGET_VSCREENINFO) failed\n");
        return -1;
    }
   LocalPrint(stderr, "newInfo:\n\t\txres = %d yres = %d bits_per_pixel = %d xres_virtual=%d, yres_virtual=%d\n",
        newInfo.xres,
        newInfo.yres,
        newInfo.bits_per_pixel, 
		newInfo.xres_virtual, 
		newInfo.yres_virtual );
   LocalPrint(stderr, "\t\txoffset:%d yoffset:%d line_length: %d\n",
     newInfo.xoffset,
     newInfo.yoffset,
     FixedInfo.line_length );


	ulVideoMemByteSize = ( newInfo.xres * newInfo.yres * newInfo.bits_per_pixel ) / 8;
    ulVideoMemByteSize = FixedInfo.smem_len;
    /*
     * fbdev says the frame buffer is at offset zero, and the mmio region
     * is immediately after.
     */
    /* mmap the framebuffer into our address space */
    FrameBuffer = (void *) mmap(0, /* start */
        ulVideoMemByteSize, /* bytes */
        PROT_READ | PROT_WRITE, /* prot */
        MAP_SHARED, /* flags */
        FrameBufferFD, /* fd */
        0 /* offset */);
     if (FrameBuffer == (void *) - 1) {
        LocalPrint(stderr, "error: unable to mmap framebuffer\n");
        return -1;
    }
	LocalPrint(stderr, "Open FrameBuffer:%p\n", FrameBuffer);
    return 0;
}

int closeFBDEV(void) {
    /* restore original variable screen info*/
    if (ioctl(FrameBufferFD, FBIOPUT_VSCREENINFO, &OrigVarInfo)) {
        LocalPrint(stderr, "ioctl(FBIOPUT_VSCREENINFO failed)\n");
        return -1;
    }
    munmap(FrameBuffer, ulVideoMemByteSize);
    close(FrameBufferFD);
 	LocalPrint(stderr, "closeFBDEV finish\n");
	return 0;
}

static void fb_update()   //将要渲染的图形缓冲区的内容绘制到设备显示屏来
{  
    newInfo.yoffset = 1;  
    ioctl(FrameBufferFD, FBIOPUT_VSCREENINFO, &newInfo);  
    newInfo.yoffset = 0;  
    ioctl(FrameBufferFD, FBIOPUT_VSCREENINFO, &newInfo);  
}  


void fb_write(uint16_t* addr, int w, int h) {
    openFBDEV();
    LocalPrint(stderr, "openFBDEV finish\n"); 
	memcpy(FrameBuffer, addr, w*h*2);
    closeFBDEV(); 
    LocalPrint(stderr, "closeFBDEV finish\n");
}

void fb_blit(uint32_t* addr, int sw, int sh) {
    openFBDEV();
    
	int dw = newInfo.xres;
	int dh = newInfo.yres;

    LocalPrint(stderr, "fb_blit: %dx%d, src:%dx%d\n", dw, dh, sw, sh);
	if(sh>dh) sh=dh;
	if(sw>dw) sw=dw;
	
	uint32_t* dst = (uint32_t*)FrameBuffer;
	uint32_t* src = addr;	

	LocalPrint(stderr, "fb_blit src:%p, %dx%d dst:%p %dx%d\n", addr, sw, sh,
		dst, dw, sh);

	int y;
	for(y=0; y<sh; y++){
		memcpy(dst, src, sw*4);
		dst += dw;
		src += sw;
	}

    closeFBDEV(); 
}



void fb_clean() {
    openFBDEV();
    
	memset(FrameBuffer, 64, ulVideoMemByteSize);
	fb_update();
    closeFBDEV(); 
	LocalPrint(stderr, "===============clean screen============\n");
}
